#pragma once
#define SCREEN_BPP 32
#define SCREEN_WIDTH 1120
#define SCREEN_HEIGHT 720

#define BOARD_ARRAY_SIZE 100 
#define NUMBER_OF_PLAYERS 4 // This will be dynamic once user can choose number of players in the title menu
#define BOARD_START_X 49
#define BOARD_START_Y 49
#define TILE_ICON_WIDTH 38
#define TILE_ICON_HEIGHT 38
#define TILE_ARROW_ROW_START 5
#define TILE_PLAYER_ROW_START 6
#define TILE_BG_WIDTH 127
#define TILE_BG_HEIGHT 127
#define TILE_IMAGE_MODIFIER_X 1
#define TILE_IMAGE_MODIFIER_Y 1
#define HAND_TILE_IMAGE_MODIFIER_X 0.7
#define HAND_TILE_IMAGE_MODIFIER_Y 0.7
#define PLAYER_TILE_LIST_START_X 70
#define PLAYER_TILE_LIST_START_Y 590
//#define PLAYER_TILE_LIST_END_X 70 PLAYER_TILE_LIST_START_X +TILE_BG_WIDTH*HAND_TILE_IMAGE_MODIFIER_X*PLAYER_TILE_LIST_DISPLAY_MAX
//#define PLAYER_TILE_LIST_END_Y 590 PLAYER_TILE_LIST_START_Y+TILE_BG_HEIGHT
#define PLAYER_TILE_LIST_DISPLAY_MAX 4
#define INITIAL_FOCUS_ROW 49
#define INITIAL_FOCUS_COLUMN 49
#define GRID_THICK_X 1
#define GRID_THICK_Y 1
#define GRID_COLUMN_COLOR red
#define GRID_ROW_COLOR red
#define CAM_MOVE_THRESHOLD 30
#define CAM_MOVE_SPEED 30
#define MINIMAP_FOCUS_WIDTH 15
#define MINIMAP_FOCUS_HEIGHT 15
#define MINIMAP_TILE_WIDTH 5
#define MINIMAP_TILE_HEIGHT 5
#define MINIMAP_START_X 520
#define MINIMAP_START_Y 560
#define MINIMAP_EMPTY_COLOR white
#define MINIMAP_TILE_COLOR blue
#define MINIMAP_PLAYER_COLOR green
#define MINIMAP_BORDER_THICKNESS 6
#define MINIMAP_BORDER_COLOR black
#define MINIMAP_CAM_COLOR black
#define OVERLAY_COLOR 0x44ff44
#define OVERLAY_ALPHA 150 // 0-255 (0 transparent, 255 no alpha)
#define DIM_BOARD_COLOR black
#define DIM_BOARD_ALPHA 60
#define DRAKON_ALPHA 180
#define SCROLL_ARROWS_ALPHA 130

// TILE ID MAPPINGS
#define START_TILE_ID	0
#define DESTROY_TILE	1 
#define MAGICAL_SHIFT	2 
#define FLOATING_TILE	3 
#define ROTATE_TILE		4
#define FIND_GOLD		5
#define STEAL_GOLD_CW	6
#define STEAL_GOLD_CCW	7
#define LOSE_A_COIN		8
#define MIND_CONTROL	9
#define MAGICAL_HARP	10
#define MAP_TILE		11
#define MOVE_DRAKON		12
#define MASTER_KEY		13
#define STRONG_WIND		14
#define ESCAPE_TILE		15
#define TELEPORT		16
#define EMPTY_TILE		17


#include <stdio.h>
#include <string>
#include <sstream>
#include <list>
#include <iostream>
#include "Color.h"
#include <map>
#include <utility>
using namespace std;


typedef std::pair<int,int> IntInt;
typedef std::map<int,int> IntIntMap;
typedef list<pair<int,int>> IntIntList;
typedef pair<bool,int> BoolInt;
typedef list<pair<bool,int>> BoolIntList;
typedef std::map<bool,int> BoolIntMap;
static enum arrowDirection{
	UP, RIGHT, DOWN, LEFT, NULL_TYPE, ROW = 5
};
static arrowDirection incArrow(arrowDirection &type){
	switch(type){
		case UP:
			type = RIGHT; 
			break;
		case RIGHT:
			type = DOWN;
			break;
		case DOWN:
			type = LEFT;
			break;
		case LEFT:
			type = NULL_TYPE;
			break;
		default:
			type = NULL_TYPE;
			break;
	}
	return type;
}
// Used often for .toString() functions
static string getIntToStr(int val){
	string s;
	stringstream out;
	out << val;
	return out.str();
}

static string toString(SDL_Rect rect){
	string s;
	s += "{";
	s += getIntToStr(rect.x);
	s += ",";
	s += getIntToStr(rect.y);
	s += ",";
	s += getIntToStr(rect.w);
	s += ",";
	s += getIntToStr(rect.h);
	s += "}";

	return s;

}
static string toString(arrowDirection type){
	string temp;
	switch(type){
		case UP:
			temp = "UP";
			break;
		case DOWN:
			temp = "DOWN";
			break;
		case RIGHT:
			temp = "RIGHT";
			break;
		case LEFT:
			temp = "LEFT";
			break;
		case NULL_TYPE:
			temp = "NULL_TYPE";
			break;
		case ROW:
			temp = "ROW";
			break;
		default:
			temp = "INVALID arrowType!";
			break;
	}
	return temp;
}
class Position
{
private:
	int m_x;
	int m_y;
public:
	Position():m_x(0),m_y(0){}
	// makes a POSITION, useful for treating POSITION as a class when passing in
	Position(int a_x, int a_y):m_x(a_x),m_y(a_y){}
	int getX(){return m_x;}
	int getY(){return m_y;}
	void setX(int a_x){m_x = a_x;}
	void setY(int a_y){m_y = a_y;}
	void setPos(int a_x,int a_y){m_x = a_x; m_y = a_y;}
	string toString(){
		string temp;
		temp += "(";
		temp += getIntToStr( m_x);
		temp += ", ";
		temp += getIntToStr( m_y);
		temp += ")";
		return temp;
	}
	bool equals(Position pos){
		return 
			m_x == pos.m_x && 
			m_y == pos.m_y;
	}
};

enum GoldValues{
	GV_ONE = 1, GV_TWO = 2, GV_THREE = 3, GV_NULL
};

// Converting CoinSack to contain a list of type GoldValues instead
class CoinSack{
private:
	list<GoldValues> goldList;
	list<GoldValues> goldLost;
	list<GoldValues> goldReceived;
public:
	CoinSack(){
		goldList.clear();
	}
	unsigned int getGoldPieceCount(){
		return (unsigned int) goldList.size();
	}
	int getSize(){return goldList.size();}
	unsigned int getGoldTotal(){
		int total = 0;
		for(list<GoldValues>::iterator it = goldList.begin(); it != goldList.end(); it++){
			total += (int) *it;
		}
		return total;
	}
	unsigned int getTotalGoldLost(){
		int total = 0;
		if(goldLost.size() > 0){
			for(list<GoldValues>::iterator it = goldLost.begin(); it != goldLost.end(); it++){
				total += (int) *it;
			}
		}
		return total;
	}
	unsigned int getTotalGoldReceived(){
		int total = 0;
		if(goldReceived.size() > 0){
			for(list<GoldValues>::iterator it = goldReceived.begin(); it != goldReceived.end(); it++){
				total += (int) *it;
			}
		}
		return total;
	}
	void addCoin(){
		int random = rand()%3; // get random number [0-3]
		switch (random){
			case 0:
				goldList.push_back(GV_ONE);
				goldReceived.push_back(GV_ONE);
				break;
			case 1:
				goldList.push_back(GV_TWO);
				goldReceived.push_back(GV_TWO);
				break;
			case 2:
				goldList.push_back(GV_THREE);
				goldReceived.push_back(GV_THREE);
				break;
		}
	}
	void addCoin(GoldValues coinToAdd){
		goldList.push_back(coinToAdd);
		goldReceived.push_back(coinToAdd);
	}

	GoldValues removeCoin(){
		if(goldList.size() > 0){
			int random = rand()%goldList.size();
			list<GoldValues>::iterator it = goldList.begin();

			for(int count = 0; count < random; it++, count++){
			}
			GoldValues returnThisPiece = *it;
			goldLost.push_back(*it);
			goldList.erase(it);
			return returnThisPiece;
		}
		else{
			return GV_NULL;
		}
	}
	string toString(){
		string s;
		stringstream out;
		out << getGoldTotal();
		return out.str();
	}

};



class Quadrants {
public:
	Position quads[9];
	Quadrants() {
		int w = TILE_BG_WIDTH;
		int h = TILE_BG_HEIGHT;
		w = int(w/3);
		h = int(h/3);
		quads[0].setPos(0*w,0*h);
		quads[1].setPos(1*w,0*h);
		quads[2].setPos(2*w,0*h);
		quads[3].setPos(0*w,1*h);
		quads[4].setPos(1*w,1*h);
		quads[5].setPos(2*w,1*h);
		quads[6].setPos(0*w,2*h);
		quads[7].setPos(1*w,2*h);
		quads[8].setPos(2*w,2*h);
	}
};

static bool isWithinRect(Position a_xy, SDL_Rect a_rect) {
	return a_xy.getX() >= a_rect.x  && 
		   a_xy.getX() <= a_rect.x  + a_rect.w &&
		   a_xy.getY() >= a_rect.y  && 
		   a_xy.getY() <= a_rect.y  + a_rect.h;
}
static bool isWithinRect(int a_x, int a_y, SDL_Rect a_rect) {
	return a_x >= a_rect.x  && 
		   a_x <= a_rect.x  + a_rect.w &&
		   a_y >= a_rect.y  && 
		   a_y <= a_rect.y  + a_rect.h;
}